/**
 * Copyright (c) 2022 Beijing Jiaotong University
 * PhotLab is licensed under [Open Source License].
 * You can use this software according to the terms and conditions of the [Open Source License].
 * You may obtain a copy of [Open Source License] at: [https://open.source.license/]
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 *
 * See the [Open Source License] for more details.
 *
 * Author: Zhenjie Wei
 * Created: Mar. 13, 2023
 * Supported by: National Key Research and Development Program of China
 */

import { Disposable } from "@/common/disposable"
import { uid_rt } from "@/common/types/base_types"
import { engineService as engineAPI } from "./client"
import { PhotTaskStatus } from "@/common/types/phot_types"

export enum TaskPriority {
    GOD,
    URGENT,
    MIDDLE,
    LOW,
}

export const engineManager = new class EngineManager extends Disposable {
    private _taskStatus = new Map<string, PhotTaskStatus>()
    private _currentTaskId: string | any = null
    private _taskIdMap: Map<string, any> = new Map()
    private _messageLog: Map<string, any> = new Map()
    constructor() {
        super()
        return
    }

    public get taskId(): string | null {
        return this._currentTaskId
    }

    public get taskIdMap(): Map<string, any> {
        return this._taskIdMap
    }

    public get taskStatus(): PhotTaskStatus | null {
        if (!this._currentTaskId) {
            console.error('currentTaskId is null.')
            return PhotTaskStatus.TASK_DIED
        }
        return this.getTaskStatus(this._currentTaskId)
    }

    public get messageLog(): any[] {
        if (!this._currentTaskId) {
            return []
        }
        return this._messageLog.get(this._currentTaskId) || []
    }

    public switchTask(graphId: string) {
        console.group("engineManager.switchTask")
        if (!this._taskIdMap.has(graphId)) {
            throw new Error("engineManager.switchTask failed. graph id not found.")
        }
        const taskId = this._taskIdMap.get(graphId)
        if (this._currentTaskId === taskId) {
            return
        }
        this._currentTaskId = taskId
        console.log("switched to task: " + taskId)
        console.groupEnd()
    }

    public async createTaskId(graphId: string) {
        const taskID = uid_rt().toString()
        this._taskIdMap.set(graphId, taskID)
        this._currentTaskId = taskID
    }

    public async addTask(photGraph: any, priority: any = 0, executionTime: any = 0) {
        await engineAPI.addTask(photGraph, this._currentTaskId, priority, executionTime)
    }

    public async createTask(graphId: string, priority: TaskPriority = TaskPriority.LOW, delay = 0) {
        console.group('engineManager.createTask')
        await engineAPI.createTaskFromExistingGraph(graphId, this._currentTaskId, priority, delay)
        console.groupEnd()
    }

    public async deleteTask(taskID: string) {
        if (this._taskStatus.has(taskID)) {
            await engineAPI.deleteTask(taskID)
            this._taskStatus.delete(taskID)
        } else {
            this._taskIdMap.delete(taskID)
        }
    }

    public getTaskStatus(graphId: string): PhotTaskStatus | null {
        const taskID = this._taskIdMap.get(graphId)
        if (!taskID) return null
        console.log(this._taskStatus.get(taskID))
        return this._taskStatus.get(taskID) || PhotTaskStatus.TASK_DIED
    }

    public async syncTaskStatus(taskID: string) {
        const status = await engineAPI.getTaskStatus(taskID)
        this._taskStatus.set(taskID, status)
        return status
    }

    public async syncAllTaskStatus() {
        // const status = await engineAPI.getAllTaskStatus()
        for (const taskID of this._taskStatus.keys()) {
            await this.syncTaskStatus(taskID)
        }
    }

    public async startTask(taskID: string | null = this._currentTaskId) {
        if (!taskID) {
            console.error('taskID is null.')
            return
        }
        await engineAPI.calculateTask(taskID)
        this._taskStatus.set(taskID, PhotTaskStatus.TASK_RUNNING)
    }

    public async pauseTask(taskID: string | null = this._currentTaskId) {
        if (!taskID) {
            console.error('taskID is null.')
            return
        }
        await engineAPI.pauseTask(taskID)
        this._taskStatus.set(taskID, PhotTaskStatus.TASK_STOPPED)
    }

    public async resumeTask(taskID: string | null = this._currentTaskId) {
        if (!taskID) {
            console.error('taskID is null.')
            return
        }
        await engineAPI.recoverTask(taskID)
        this._taskStatus.set(taskID, PhotTaskStatus.TASK_RUNNING)
    }

    public async restartTask(taskID: string | null = this._currentTaskId) {
        if (!taskID) {
            console.error('taskID is null.')
            return
        }
        await engineAPI.restartTask(taskID)
        this._taskStatus.set(taskID, PhotTaskStatus.TASK_RUNNING)
    }

    public async cancelTask(taskID: string | null = this._currentTaskId) {
        if (!taskID) {
            console.error('taskID is null.')
            return
        }
        await engineAPI.cancelTask(taskID)
        this._taskStatus.set(taskID, PhotTaskStatus.TASK_CREATING)
    }

    public async getTaskExceptionInformation(taskID: string | null = this._currentTaskId) {
        if (!taskID) {
            console.error('taskID is null.')
            return
        }
        this._taskStatus.set(taskID, PhotTaskStatus.TASK_ACCOMPLISHED)
        try {
            const information = await engineAPI.getTaskExceptionInformation(taskID)
            this._messageLog.set(taskID, information)
            // console.log(this._messageLog)
            return information
        } catch (err) {
            return err
        }
    }

    public async getComponentOutput(componentId: number) {
        if (!this._currentTaskId) {
            console.error('currentTaskId is null.')
            return ''
        }
        return await engineAPI.getComponentOutput(this._currentTaskId, componentId)
    }

    public async getAnalyzerOutput(componentId: number) {
        if (!this._currentTaskId) {
            console.error('currentTaskId is null.')
            return ''
        }
        return await engineAPI.getAnalyzerOutput(this._currentTaskId, componentId)
    }

    // public async getAllComponentOutput() {
    //     if (!this._currentTaskId){
    //         console.error('currentTaskId is null.')
    //         return ''
    //     }
    //     return await engineAPI.getAllComponentOutput(this._currentTaskId)
    // }

    public async saveComponentOutputAs(componentId: number, filePath: string) {
        if (!this._currentTaskId) {
            console.error('currentTaskId is null.')
            return ''
        }
        return await engineAPI.saveComponentOutputAs(this._currentTaskId, componentId, filePath)
    }

    public async saveAnalyzerResultAs(componentId: number, filePath: string) {
        if (!this._currentTaskId) {
            console.error('currentTaskId is null.')
            return ''
        }
        return await engineAPI.saveAnalyzerResultAs(this._currentTaskId, componentId, filePath)
    }

    public activate() {
        return
    }
}